package com.gufe.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import com.itextpdf.text.pdf.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.ResourceUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

@Slf4j
public class PdfUtils {
    private static boolean isPrimitiveOrWrapper(Class<?> clazz) {  //检查传入的类是否为基本类型或基本包装类型
        return clazz.isPrimitive() || clazz.getName().startsWith("java.lang");
    }

    private static Map<String,String> turnMap(Object object) {
        Map<String,Object> stringObjectMap = BeanUtil.beanToMap(object);  //把对象转为Map
        Map<String,String> map = new HashMap<>(stringObjectMap.size()*2);
        //打印输出属性名称和属性值
        stringObjectMap.forEach((k,v)->{
            if(ObjectUtil.isNotEmpty(v)) {
                //基本类型和封装类型
                if (isPrimitiveOrWrapper(v.getClass())) {
                    map.put(k, String.valueOf(v));
                } else {
                    //其他类型
                    if (v instanceof List) {
                        List<Object> list = (List) v;
                        for (int i = 0; i < list.size(); i++) {
                            Object o = list.get(i);
                            Map<String, Object> stringObjectMap1 = BeanUtil.beanToMap(o);
                            int finalI = i;
                            stringObjectMap1.forEach((k1, v1) -> {
                                map.put(StrUtil.format("{}.{}{}", k, k1, finalI), String.valueOf(v1));
                            });
                        }
                    } else {
                        Map<String, Object> stringObjectMap1 = BeanUtil.beanToMap(v);
                        stringObjectMap1.forEach((k1, v1) -> {
                            map.put(StrUtil.format("{}.{}", k, k1), String.valueOf(v1));
                        });
                    }
                }
            }
        });
        return map;
    }

    //生成pdf
    public static void generatePdf(HttpServletRequest request,
                                   HttpServletResponse response,
                                   String templatePath,//当前为文件名，之后改为文件路径
                                   Object object,
                                   String newFileName) {
        try(OutputStream responseOutputStream = response.getOutputStream(); ByteArrayOutputStream fileOut= new ByteArrayOutputStream()){
            if(!newFileName.endsWith(".pdf")) newFileName=newFileName+".pdf"; //
            //模版在项目中的位置
            Resource resource = new PathMatchingResourcePatternResolver().getResource(ResourceUtils.CLASSPATH_URL_PREFIX + "pdf/" + templatePath);
            PdfReader pdfReader = new PdfReader(resource.getInputStream());
            PdfStamper pdfStamper = new PdfStamper(pdfReader, fileOut);

            BaseFont font = BaseFont.createFont("C:/Windows/Fonts/simsun.ttc,0", "Identity-H", BaseFont.EMBEDDED);
            ArrayList<BaseFont> fontList = new ArrayList<>();
            fontList.add(font);

            //取出报表模版中的所有字段
            AcroFields fields = pdfStamper.getAcroFields();
            fields.setSubstitutionFonts(fontList);
            PdfUtils.fillData(fields,PdfUtils.turnMap(object),pdfStamper);
            //必须要调用这个，否则文档不会生成的，如果为false，生成的pdf还能编辑，所以设置为true
            pdfStamper.setFormFlattening(true);
            pdfStamper.close();
            writeFile(request,response,fileOut,newFileName);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 填充数据
     */
    private static void fillData(AcroFields fields,Map<String,String> data,PdfStamper pdfStamper) throws IOException, DocumentException {
        Map<String, AcroFields.Item> formFields = fields.getFields();
        // 创建基础字体（示例使用宋体）
        BaseFont font = BaseFont.createFont("C:/Windows/Fonts/simsun.ttc,0", "Identity-H", BaseFont.EMBEDDED);
        ArrayList<BaseFont> fontList = new ArrayList<>();
        fontList.add(font);
        String v;
        for (String k : data.keySet()) {
            if(formFields.containsKey(k)){
                // 获取字段字典
                PdfDictionary dict = formFields.get(k).getWidget(0);
                // 创建新的字体字典
                PdfDictionary daDict = new PdfDictionary();
                daDict.put(PdfName.DA, new PdfString("0 g /F1 10 Tf")); // F1对应字体索引，10为字号
                // 合并字典
                dict.merge(daDict);

                // 设置字段值
                fields.setFieldProperty(k, "textfont", font, null);
                fields.setField(k, data.get(k));
            }
        }
        String imgUrl = data.get("imgUrl"); // 直接从 data 中获取
        if (StrUtil.isNotBlank(imgUrl)) {
            try {
                // 1. 下载图片
                byte[] imageBytes = downloadImage(imgUrl);
                Image image = Image.getInstance(imageBytes);

                // 2. 获取表单域的位置信息
                String imageFieldName = "imgUrl"; // 匹配模板中的字段名
                List<AcroFields.FieldPosition> positions = fields.getFieldPositions(imageFieldName);

                if (positions != null && !positions.isEmpty()) {
                    AcroFields.FieldPosition position = positions.get(0);
                    Rectangle rect = position.position; // 获取字段的坐标和尺寸

                    // 3. 缩放图片适应字段区域
                    image.scaleToFit(rect.getWidth(), rect.getHeight());
                    image.setAbsolutePosition(rect.getLeft(), rect.getBottom()); // 左下角坐标

                    // 4. 插入图片到字段所在页面
                    PdfContentByte content = pdfStamper.getOverContent(position.page);
                    content.addImage(image);

                    // 5. 移除表单域（避免覆盖图片）
                    fields.removeField(imageFieldName);
                } else {
                    log.warn("未找到图片占位字段: {}", imageFieldName);
                }
            } catch (Exception e) {
                log.error("插入图片失败: {}", imgUrl, e);
            }
        }

    }

    private static void writeFile(HttpServletRequest request,
                                  HttpServletResponse response,
                                  ByteArrayOutputStream fileOut,
                                  String newFileName) throws IOException{

        response.setCharacterEncoding(request.getCharacterEncoding());
        response.setContentType("application/pdf");
        try{
            //这里主要防止下载的pdf文件名乱码
            response.setHeader("Content-Disposition","attachment; filename="+ URLEncoder.encode(newFileName, StandardCharsets.UTF_8));
            fileOut.writeTo(response.getOutputStream());
            response.flushBuffer();
        }catch (Exception e){
            log.error("pdf生成异常");
            throw new RuntimeException("pdf生成异常");
        }

    }

    // 根据URl下载图片 并转化为字符流
    private static byte[] downloadImage(String imgUrl) throws IOException {
        int maxRetries = 3;
        int retryCount = 0;
        while (retryCount < maxRetries) {
            try {
                URL url = new URL(imgUrl);
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setRequestMethod("GET");
                connection.setConnectTimeout(5000);
                connection.setReadTimeout(5000);
                connection.setRequestProperty("User-Agent", "Mozilla/5.0");

                if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
                    try (InputStream inputStream = connection.getInputStream();
                         ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                        byte[] buffer = new byte[4096];
                        int bytesRead;
                        while ((bytesRead = inputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);
                        }
                        return outputStream.toByteArray();
                    }
                } else {
                    retryCount++;
                    Thread.sleep(1000 * retryCount);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new IOException("Download interrupted", e);
            } catch (IOException e) {
                retryCount++;
                if (retryCount >= maxRetries) {
                    throw new IOException("Failed to download image after retries", e);
                }
            }
        }
        throw new IOException("All retries exhausted");
    }

}